// 版权所有2010 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package time

import "errors"

// 这些是为及时使用而预定义的布局。格式和时间。作语法分析
// 这些布局中使用的参考时间是特定的时间戳：
// 01/02 03:04:05PM'06-0700 
// （2006年1月2日15:04:05，在格林威治标准时间以西七小时的时区）。
// 该值被记录为名为Layout的常量，如下所示。作为Unix 
// 时间，这里是1136239445。由于MST是GMT-0700，所以引用将是
// 由Unix date命令打印为：
// Mon Jan 2 15:04:05 MST 2006 
// 这是一个令人遗憾的历史错误，日期使用了美国惯例
// 将数字月放在日期之前。
// 
// 时间的例子。Format详细演示了布局字符串
// 的工作原理，是一个很好的参考。
// 
// 请注意，RFC822、RFC850和RFC1123格式应仅适用于当地时间。将其应用于UTC时报将使用“UTC”作为
// 时区的缩写，而严格来说，这些RFC要求在这种情况下使用“GMT”。
// 对于坚持该格式的服务器
// 通常应使用RFC1123Z而不是RFC1123，对于新协议，应首选RFC3339。
// RFC3339、RFC822、RFC822Z、RFC1123和RFC1123Z用于格式化；
// 与时间一起使用时。解析他们不接受RFC允许的所有时间格式
// 并且他们接受未正式定义的时间格式。
// RFC3339Nano格式会从秒字段中删除尾随的零
// 因此，一旦格式化，可能无法正确排序。
// 
// 大多数程序都可以使用其中一个定义的常量作为传递给
// 格式或解析的布局。除非您正在创建自定义布局字符串，否则可以忽略此注释的其余部分。
// 
// 要定义自己的格式，请写下参考时间的样子
// 按自己的方式格式化；有关示例，请参见ANSIC、StampMicro或
// Kitchen等常量的值。该模型将演示参考时间
// 的外观，以便格式和解析方法可以对一般时间值应用相同的
// 转换。
// 
// 这里是布局字符串组件的摘要。每个元素通过
// 示例显示参考时间元素的格式。只有这些值
// 被识别。布局字符串中的文本未被识别为
// 参考时间在格式化过程中逐字回显，预计将在要解析的输入中逐字显示
// 参考时间。
// 
// 年份：“2006”“06”
// 月份：“1月”“1月”
// 一周中的文字日期：“周一”“周一”
// 一个月中的数字日期：“2”“02”
// 一年中的数字日期：“u 2”“002”
// 小时：“15”“3”“03”（下午或上午）
// AM/PM标记：“PM”
// 
// 数字时区偏移格式如下：
// “-0700”±hhmm 
// “-07:00”±hh:mm 
// “-07”±hh 
// ISO 8601打印Z而非
// /“Z0700”Z或±hhmm 
// /“Z07:00”Z或±hh:mm 
// /“Z07”Z或±hh 
// 
// 在格式字符串中，“_2”和“u 2”中的下划线代表空格
// 如果以下数字有多个数字，则可以用数字替换，
// 与固定宽度Unix时间格式兼容。前导零表示
// 一个零填充值。
// 
// 格式_2和002为空格填充和零填充
// 一年中的三个字符日；没有未添加的年度日期格式。
// 
// 一个逗号或小数点后跟一个或多个零表示
// 一个小数秒，按给定的小数位数打印。
// 一个逗号或小数点后跟一个或多个9表示
// 一个小数点，打印到给定的小数位数，带有
// 删除尾随的零。
// 例如“15:04:05000”或“15:04:05.000”格式或解析的精度为毫秒。
// 
// 一些有效的布局对于时间来说是无效的时间值。解析，由于
// 格式，例如u表示空格填充，Z表示区域信息。
// 
const (
	Layout      = "01/02 03:04:05PM '06 -0700" // 参考时间，以数字顺序。
	ANSIC       = "Mon Jan _2 15:04:05 2006"
	UnixDate    = "Mon Jan _2 15:04:05 MST 2006"
	RubyDate    = "Mon Jan 02 15:04:05 -0700 2006"
	RFC822      = "02 Jan 06 15:04 MST"
	RFC822Z     = "02 Jan 06 15:04 -0700" // RFC822带数字区
	RFC850      = "Monday, 02-Jan-06 15:04:05 MST"
	RFC1123     = "Mon, 02 Jan 2006 15:04:05 MST"
	RFC1123Z    = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123带数字区
	RFC3339     = "2006-01-02T15:04:05Z07:00"
	RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
	Kitchen     = "3:04PM"
	// 方便的时间戳。“1”
	Stamp      = "Jan _2 15:04:05"
	StampMilli = "Jan _2 15:04:05.000"
	StampMicro = "Jan _2 15:04:05.000000"
	StampNano  = "Jan _2 15:04:05.000000000"
)

const (
	_                        = iota
	stdZeroMinute                                  // /“04”
	stdSecond                                      // /“5”
	stdZeroSecond                                  // /“05”
	stdLongYear              = iota + stdNeedDate  // /“2006”
	stdYear                                        // /“06”
	stdPM                    = iota + stdNeedClock // /“PM”
	stdTZ                    = iota                // /“MST”
	stdNumShortTZ                                  // /”-07“
	stdNumColonTZ                                  // /”-07:00“
	stdNumColonSecondsTZ                           // /”-07:00:00“
	stdFracSecond0                                 // /”.0“，.00”，“.00”，尾随的零包括
	stdFracSecond9                                 // /“.9”、“.99”、。。。，尾随零省略

	stdNeedDate       = 1 << 8             // 需要月、日、年
	stdNeedClock      = 2 << 8             // 需要小时、分钟、秒
	stdArgShift       = 16                 // 高位额外参数，高于低位标准移位
	stdSeparatorShift = 28                 // 分数秒分隔符高4位额外参数
	stdMask           = 1<<stdArgShift - 1 // 屏蔽参数
)

// std0x记录“01”、“02”和。。。，"06". 
var std0x = [...]int{stdZeroMonth, stdZeroDay, stdZeroHour12, stdZeroMinute, stdZeroSecond, stdYear}

// startsWithLowerCase报告字符串开头是否有小写字母。
// 其目的是防止在查找“Mon”时匹配“Month”等字符串。
func startsWithLowerCase(str string) bool {
	if len(str) == 0 {
		return false
	}
	c := str[0]
	return 'a' <= c && c <= 'z'
}

// nextStdChunk在
// layout中查找第一个出现的std字符串，并返回前面的文本、std字符串和后面的文本。
func nextStdChunk(layout string) (prefix string, std int, suffix string) {
	for i := 0; i < len(layout); i++ {
		switch c := int(layout[i]); c {
		case 'J': // 1月
			if len(layout) >= i+3 && layout[i:i+3] == "Jan" {
				if len(layout) >= i+7 && layout[i:i+7] == "January" {
					return layout[0:i], stdLongMonth, layout[i+7:]
				}
				if !startsWithLowerCase(layout[i+3:]) {
					return layout[0:i], stdMonth, layout[i+3:]
				}
			}

		case 'M': // MST周一
			if len(layout) >= i+3 {
				if layout[i:i+3] == "Mon" {
					if len(layout) >= i+6 && layout[i:i+6] == "Monday" {
						return layout[0:i], stdLongWeekDay, layout[i+6:]
					}
					if !startsWithLowerCase(layout[i+3:]) {
						return layout[0:i], stdWeekDay, layout[i+3:]
					}
				}
				if layout[i:i+3] == "MST" {
					return layout[0:i], stdTZ, layout[i+3:]
				}
			}

		case '0': // 01,02,03,04,05,06,002 
			if len(layout) >= i+2 && '1' <= layout[i+1] && layout[i+1] <= '6' {
				return layout[0:i], std0x[layout[i+1]-'1'], layout[i+2:]
			}
			if len(layout) >= i+3 && layout[i+1] == '0' && layout[i+2] == '2' {
				return layout[0:i], stdZeroYearDay, layout[i+3:]
			}

			if len(layout) >= i+2 && layout[i+1] == '5' {
				return layout[0:i], stdHour, layout[i+2:]
			}
			return layout[0:i], stdNumMonth, layout[i+1:]

		case '2': // 2006,2 
			if len(layout) >= i+4 && layout[i:i+4] == "2006" {
				return layout[0:i], stdLongYear, layout[i+4:]
			}
			return layout[0:i], stdDay, layout[i+1:]

		case '_': // 2,2006,2 
			if len(layout) >= i+2 && layout[i+1] == '2' {
				// 2006真的是一个字面意义上的uuuuu2006;，接下来是一年
				if len(layout) >= i+5 && layout[i+1:i+5] == "2006" {
					return layout[0 : i+1], stdLongYear, layout[i+5:]
				}
				return layout[0:i], stdUnderDay, layout[i+2:]
			}
			if len(layout) >= i+3 && layout[i+1] == '_' && layout[i+2] == '2' {
				return layout[0:i], stdUnderYearDay, layout[i+3:]
			}

		case '3':
			return layout[0:i], stdHour12, layout[i+1:]

		case '4':
			return layout[0:i], stdMinute, layout[i+1:]

		case '5':
			return layout[0:i], stdSecond, layout[i+1:]

			if len(layout) >= i+2 && layout[i+1] == 'M' {
				return layout[0:i], stdPM, layout[i+2:]
			}

			if len(layout) >= i+2 && layout[i+1] == 'm' {
				return layout[0:i], stdpm, layout[i+2:]
			}

			if len(layout) >= i+7 && layout[i:i+7] == "-070000" {
				return layout[0:i], stdNumSecondsTz, layout[i+7:]
			}
			if len(layout) >= i+9 && layout[i:i+9] == "-07:00:00" {
				return layout[0:i], stdNumColonSecondsTZ, layout[i+9:]
			}
			if len(layout) >= i+5 && layout[i:i+5] == "-0700" {
				return layout[0:i], stdNumTZ, layout[i+5:]
			}
			if len(layout) >= i+6 && layout[i:i+6] == "-07:00" {
				return layout[0:i], stdNumColonTZ, layout[i+6:]
			}
			if len(layout) >= i+3 && layout[i:i+3] == "-07" {
				return layout[0:i], stdNumShortTZ, layout[i+3:]
			}

		case 'Z': // Z070000，Z07:00:00，Z0700，Z07:00，
			if len(layout) >= i+7 && layout[i:i+7] == "Z070000" {
				return layout[0:i], stdISO8601SecondsTZ, layout[i+7:]
			}
			if len(layout) >= i+9 && layout[i:i+9] == "Z07:00:00" {
				return layout[0:i], stdISO8601ColonSecondsTZ, layout[i+9:]
			}
			if len(layout) >= i+5 && layout[i:i+5] == "Z0700" {
				return layout[0:i], stdISO8601TZ, layout[i+5:]
			}
			if len(layout) >= i+6 && layout[i:i+6] == "Z07:00" {
				return layout[0:i], stdISO8601ColonTZ, layout[i+6:]
			}
			if len(layout) >= i+3 && layout[i:i+3] == "Z07" {
				return layout[0:i], stdISO8601ShortTZ, layout[i+3:]
			}

		case '.', ',': // ，000，或.000，或.999，或.999-小数秒的重复数字。
			if i+1 < len(layout) && (layout[i+1] == '0' || layout[i+1] == '9') {
				ch := layout[i+1]
				j := i + 1
				for j < len(layout) && layout[j] == ch {
					j++
				}
				// 数字串必须在这里结束-只有小数秒是所有数字。
				if !isDigit(layout, j) {
					code := stdFracSecond0
					if layout[i+1] == '9' {
						code = stdFracSecond9
					}
					std := stdFracSecond(code, j-(i+1), c)
					return layout[0:i], std, layout[j:]
				}
			}
		}
	}
	return layout, 0, ""
}

var longDayNames = []string{
	"Sunday",
	"Monday",
	"Tuesday",
	"Wednesday",
	"Thursday",
	"Friday",
	"Saturday",
}

var shortDayNames = []string{
	"Sun",
	"Mon",
	"Tue",
	"Wed",
	"Thu",
	"Fri",
	"Sat",
}

var shortMonthNames = []string{
	"Jan",
	"Feb",
	"Mar",
	"Apr",
	"May",
	"Jun",
	"Jul",
	"Aug",
	"Sep",
	"Oct",
	"Nov",
	"Dec",
}

var longMonthNames = []string{
	"January",
	"February",
	"March",
	"April",
	"May",
	"June",
	"July",
	"August",
	"September",
	"October",
	"November",
	"December",
}

// 匹配报告s1和s2是否匹配忽略大小写。
// 假设s1和s2的长度相同。
func match(s1, s2 string) bool {
	for i := 0; i < len(s1); i++ {
		c1 := s1[i]
		c2 := s2[i]
		if c1 != c2 {
			// 切换到小写；'已知a'-'a'是一个位。
			c1 |= 'a' - 'A'
			c2 |= 'a' - 'A'
			if c1 != c2 || c1 < 'a' || c1 > 'z' {
				return false
			}
		}
	}
	return true
}

func lookup(tab []string, val string) (int, string, error) {
	for i, v := range tab {
		if len(val) >= len(v) && match(val[0:len(v)], v) {
			return i, val[len(v):], nil
		}
	}
	return -1, val, errBad
}

// appendInt将x的十进制形式追加到b并返回结果。
// 如果十进制形式（不含符号）小于宽度，则结果将填充前导0。
// 复制strconv中的功能，但避免依赖关系。
func appendInt(b []byte, x int, width int) []byte {
	u := uint(x)
	if x < 0 {
		b = append(b, '-')
		u = uint(-x)
	}

	// 按相反顺序组装十进制数。
	var buf [20]byte
	i := len(buf)
	for u >= 10 {
		i--
		q := u / 10
		buf[i] = byte('0' + u - q*10)
		u = q
	}
	i--
	buf[i] = byte('0' + u)

	// 添加0填充。
	for w := len(buf) - i; w < width; w++ {
		b = append(b, '0')
	}

	return append(b, buf[i:]...)
}

// 从未打印，只需非零即可通过atoi返回。
var atoiError = errors.New("time: invalid number")

// 复制strconv中的功能，但避免依赖性。
func atoi(s string) (x int, err error) {
	neg := false
	if s != "" && (s[0] == '-' || s[0] == '+') {
		neg = s[0] == '-'
		s = s[1:]
	}
	q, rem, err := leadingInt(s)
	x = int(q)
	if err != nil || rem != "" {
		return 0, atoiError
	}
	if neg {
		x = -x
	}
	return x, nil
}

// 传递给formatNano的“std”值包含两个压缩字段：小数点后的
// 位数和分隔符（句点或逗号）。
// 这些函数打包和解包该变量。
func stdFracSecond(code, n, c int) int {
	// 使用0xfff使失败案例更加荒谬。
	if c == '.' {
		return code | ((n & 0xfff) << stdArgShift)
	}
	return code | ((n & 0xfff) << stdArgShift) | 1<<stdSeparatorShift
}

func digitsLen(std int) int {
	return (std >> stdArgShift) & 0xfff
}

func separator(std int) byte {
	if (std >> stdSeparatorShift) == 0 {
		return '.'
	}
	return ','
}

// formatNano在b 
// 后面加上一个小数秒，作为纳秒，并返回结果。
func formatNano(b []byte, nanosec uint, std int) []byte {
	var (
		n         = digitsLen(std)
		separator = separator(std)
		trim      = std&stdMask == stdFracSecond9
	)
	u := nanosec
	var buf [9]byte
	for start := len(buf); start > 0; {
		start--
		buf[start] = byte(u%10 + '0')
		u /= 10
	}

	if n > 9 {
		n = 9
	}
	if trim {
		for n > 0 && buf[n-1] == '0' {
			n--
		}
		if n == 0 {
			return b
		}
	}
	b = append(b, separator)
	return append(b, buf[:n]...)
}

// String返回使用格式字符串
// “2006-01-02 15:04:05.9999999-0700 MST”格式的时间
// 
// 如果时间具有单调的时钟读数，则返回的字符串
// 包含最后一个字段“m=±<value>”，其中值是单调的
// 时钟读数，格式为十进制秒数。
// 
// 返回的字符串用于调试；对于稳定的序列化
// 表示，请使用t.MarshalText、t.MarshalBinary或t.Format 
// 和显式格式字符串。
func (t Time) String() string {
	s := t.Format("2006-01-02 15:04:05.999999999 -0700 MST")

	// 将单调时钟读数格式化为m=±ddd。nnnnnnnnnn。
	if t.wall&hasMonotonic != 0 {
		m2 := uint64(t.ext)
		sign := byte('+')
		if t.ext < 0 {
			sign = '-'
			m2 = -m2
		}
		m1, m2 := m2/1e9, m2%1e9
		m0, m1 := m1/1e9, m1%1e9
		buf := make([]byte, 0, 24)
		buf = append(buf, " m="...)
		buf = append(buf, sign)
		wid := 0
		if m0 != 0 {
			buf = appendInt(buf, int(m0), 0)
			wid = 9
		}
		buf = appendInt(buf, int(m1), wid)
		buf = append(buf, '.')
		buf = appendInt(buf, int(m2), 9)
		s += string(buf)
	}
	return s
}

// GoString实现fmt。GoStringer和格式t将以Go source 
// 代码。
func (t Time) GoString() string {
	buf := make([]byte, 0, 70)
	buf = append(buf, "time.Date("...)
	buf = appendInt(buf, t.Year(), 0)
	month := t.Month()
	if January <= month && month <= December {
		buf = append(buf, ", time."...)
		buf = append(buf, t.Month().String()...)
	} else {
		// 很难构建一个时间。时间与
		// 标准范围之外的日期有关，但我们不妨尝试处理此案。
		buf = appendInt(buf, int(month), 0)
	}
	buf = append(buf, ", "...)
	buf = appendInt(buf, t.Day(), 0)
	buf = append(buf, ", "...)
	buf = appendInt(buf, t.Hour(), 0)
	buf = append(buf, ", "...)
	buf = appendInt(buf, t.Minute(), 0)
	buf = append(buf, ", "...)
	buf = appendInt(buf, t.Second(), 0)
	buf = append(buf, ", "...)
	buf = appendInt(buf, t.Nanosecond(), 0)
	buf = append(buf, ", "...)
	switch loc := t.Location(); loc {
	case UTC, nil:
		buf = append(buf, "time.UTC"...)
	case Local:
		buf = append(buf, "time.Local"...)
	default:
		// 我们有几个选项可以显示这个，没有一个是好的：
		// 
		// /-使用Location（loc.name），这在技术上是无效的语法
		// /-使用LoadLocation（loc.name），这将在嵌入
		// 时导致语法错误，还将要求我们在没有
		// 导入fmt或strconv 
		// /-尝试使用FixedZone的情况下转义字符串，这也需要转义名称
		// 并代表例如“America/Los_Angeles”夏令时
		// 移位不准确
		// -使用指针格式，这并不比
		// 旧fmt更糟糕。Sprintf（“%#v”，t）格式。
		// 
		// 其中，位置（loc.name）的破坏性最小。这是一个edge 
		// 案例，我们希望不要太频繁。
		buf = append(buf, `time.Location(`...)
		buf = append(buf, []byte(quote(loc.name))...)
		buf = append(buf, `)`...)
	}
	buf = append(buf, ')')
	return string(buf)
}

// Format将时间值的文本表示形式返回到参数定义的布局，该时间值是根据
// 格式化的。参见
// 常量调用布局的文档，了解如何表示布局格式。
// 
// 时间的可执行示例。格式详细演示了布局字符串的工作原理，是一个很好的参考。
func (t Time) Format(layout string) string {
	const bufSize = 64
	var b []byte
	max := len(layout) + 10
	if max < bufSize {
		var buf [bufSize]byte
		b = buf[:0]
	} else {
		b = make([]byte, 0, max)
	}
	b = t.AppendFormat(b, layout)
	return string(b)
}

// AppendFormat与Format类似，但将文本
// 表示形式附加到b并返回扩展缓冲区。
func (t Time) AppendFormat(b []byte, layout string) []byte {
	var (
		name, offset, abs = t.locabs()

		year  int = -1
		month Month
		day   int
		yday  int
		hour  int = -1
		min   int
		sec   int
	)
	// 每次迭代生成一个std值。
	for layout != "" {
		prefix, std, suffix := nextStdChunk(layout)
		if prefix != "" {
			b = append(b, prefix...)
		}
		if std == 0 {
			break
		}
		layout = suffix

		// 如果需要，计算年、月、日。
		if year < 0 && std&stdNeedDate != 0 {
			year, month, day, yday = absDate(abs, true)
			yday++
		}

		// 如果需要，计算小时、分钟、秒。
		if hour < 0 && std&stdNeedClock != 0 {
			hour, min, sec = absClock(abs)
		}

		switch std & stdMask {
		case stdYear:
			y := year
			if y < 0 {
				y = -y
			}
			b = appendInt(b, y%100, 2)
		case stdLongYear:
			b = appendInt(b, year, 4)
		case stdMonth:
			b = append(b, month.String()[:3]...)
		case stdLongMonth:
			m := month.String()
			b = append(b, m...)
		case stdNumMonth:
			b = appendInt(b, int(month), 0)
		case stdZeroMonth:
			b = appendInt(b, int(month), 2)
		case stdWeekDay:
			b = append(b, absWeekday(abs).String()[:3]...)
		case stdLongWeekDay:
			s := absWeekday(abs).String()
			b = append(b, s...)
		case stdDay:
			b = appendInt(b, day, 0)
		case stdUnderDay:
			if day < 10 {
				b = append(b, ' ')
			}
			b = appendInt(b, day, 0)
		case stdZeroDay:
			b = appendInt(b, day, 2)
		case stdUnderYearDay:
			if yday < 100 {
				b = append(b, ' ')
				if yday < 10 {
					b = append(b, ' ')
				}
			}
			b = appendInt(b, yday, 0)
		case stdZeroYearDay:
			b = appendInt(b, yday, 3)
		case stdHour:
			b = appendInt(b, hour, 2)
		case stdHour12:
			// 中午是下午12点，午夜是上午12点。
			hr := hour % 12
			if hr == 0 {
				hr = 12
			}
			b = appendInt(b, hr, 0)
		case stdZeroHour12:
			// 中午是下午12点，午夜是上午12点。
			hr := hour % 12
			if hr == 0 {
				hr = 12
			}
			b = appendInt(b, hr, 2)
		case stdMinute:
			b = appendInt(b, min, 0)
		case stdZeroMinute:
			b = appendInt(b, min, 2)
		case stdSecond:
			b = appendInt(b, sec, 0)
		case stdZeroSecond:
			b = appendInt(b, sec, 2)
		case stdPM:
			if hour >= 12 {
				b = append(b, "PM"...)
			} else {
				b = append(b, "AM"...)
			}
		case stdpm:
			if hour >= 12 {
				b = append(b, "pm"...)
			} else {
				b = append(b, "am"...)
			}
		case stdISO8601TZ, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ShortTZ, stdISO8601ColonSecondsTZ, stdNumTZ, stdNumColonTZ, stdNumSecondsTz, stdNumShortTZ, stdNumColonSecondsTZ:
			// 丑陋的特例。我们作弊，将“Z”变体
			// 理解为“ISO 8601格式的时区”。
			if offset == 0 && (std == stdISO8601TZ || std == stdISO8601ColonTZ || std == stdISO8601SecondsTZ || std == stdISO8601ShortTZ || std == stdISO8601ColonSecondsTZ) {
				b = append(b, 'Z')
				break
			}
			zone := offset / 60 // 转换为分钟
			absoffset := offset
			if zone < 0 {
				b = append(b, '-')
				zone = -zone
				absoffset = -absoffset
			} else {
				b = append(b, '+')
			}
			b = appendInt(b, zone/60, 2)
			if std == stdISO8601ColonTZ || std == stdNumColonTZ || std == stdISO8601ColonSecondsTZ || std == stdNumColonSecondsTZ {
				b = append(b, ':')
			}
			if std != stdNumShortTZ && std != stdISO8601ShortTZ {
				b = appendInt(b, zone%60, 2)
			}

			// 如果合适的话追加秒
			if std == stdISO8601SecondsTZ || std == stdNumSecondsTz || std == stdNumColonSecondsTZ || std == stdISO8601ColonSecondsTZ {
				if std == stdNumColonSecondsTZ || std == stdISO8601ColonSecondsTZ {
					b = append(b, ':')
				}
				b = appendInt(b, absoffset%60, 2)
			}

		case stdTZ:
			if name != "" {
				b = append(b, name...)
				break
			}
			// 目前没有已知的时区，但我们必须打印一个。
			// 使用-0700格式。
			zone := offset / 60 // 转换为分钟
			if zone < 0 {
				b = append(b, '-')
				zone = -zone
			} else {
				b = append(b, '+')
			}
			b = appendInt(b, zone/60, 2)
			b = appendInt(b, zone%60, 2)
		case stdFracSecond0, stdFracSecond9:
			b = formatNano(b, uint(t.Nanosecond()), std)
		}
	}
	return b
}

var errBad = errors.New("bad value for field") // 占位符未传递给用户

// ParseError描述了解析时间字符串时出现的问题。
type ParseError struct {
	Layout     string
	Value      string
	LayoutElem string
	ValueElem  string
	Message    string
}

// 这些都是从unicode/utf8和strconv中借来的，并且复制了
// 这个包中的行为，因为我们不能依赖于这两个包。
const (
	lowerhex  = "0123456789abcdef"
	runeSelf  = 0x80
	runeError = '\uFFFD'
)

func quote(s string) string {
	buf := make([]byte, 1, len(s)+2) // 切片将至少为len（s）+引号
	buf[0] = '"'
	for i, c := range s {
		if c >= runeSelf || c < ' ' {
			// 这意味着您要求我们解析一个时间。持续时间或
			// 时间。包含不可打印或非ASCII字符的位置。
			// 我们不希望经常碰到这种情况。我们可以试着复制strconv。Quote的行为非常逼真，但
			// 考虑到我们很少期望达到这些边缘情况，速度和
			// 简洁性更好。
			var width int
			if c == runeError {
				width = 1
				if i+2 < len(s) && s[i:i+3] == string(runeError) {
					width = 3
				}
			} else {
				width = len(string(c))
			}
			for j := 0; j < width; j++ {
				buf = append(buf, `\x`...)
				buf = append(buf, lowerhex[s[i+j]>>4])
				buf = append(buf, lowerhex[s[i+j]&0xF])
			}
		} else {
			if c == '"' || c == '\\' {
				buf = append(buf, '\\')
			}
			buf = append(buf, string(c)...)
		}
	}
	buf = append(buf, '"')
	return string(buf)
}

// Error返回ParseError的字符串表示形式。
func (e *ParseError) Error() string {
	if e.Message == "" {
		return "parsing time " +
			quote(e.Value) + " as " +
			quote(e.Layout) + ": cannot parse " +
			quote(e.ValueElem) + " as " +
			quote(e.LayoutElem)
	}
	return "parsing time " +
		quote(e.Value) + e.Message
}

// isDigit报告s[i]是否在范围内，是否为十进制数字。
func isDigit(s string, i int) bool {
	if len(s) <= i {
		return false
	}
	c := s[i]
	return '0' <= c && c <= '9'
}

// getnum将s[0:1]或s[0:2]（固定力s[0:2]）解析为十进制整数，并返回整数和字符串的剩余部分。
func getnum(s string, fixed bool) (int, string, error) {
	if !isDigit(s, 0) {
		return 0, s, errBad
	}
	if !isDigit(s, 1) {
		if fixed {
			return 0, s, errBad
		}
		return int(s[0] - '0'), s[1:], nil
	}
	return int(s[0]-'0')*10 + int(s[1]-'0'), s[2:], nil
}

// getnum3将[0:1]、s[0:2]或s[0:3]（固定力s[0:3]）解析为十进制整数，并返回字符串的整数和余数
// 。
func getnum3(s string, fixed bool) (int, string, error) {
	var n, i int
	for i = 0; i < 3 && isDigit(s, i); i++ {
		n = n*10 + int(s[i]-'0')
	}
	if i == 0 || fixed && i != 3 {
		return 0, s, errBad
	}
	return n, s[i:], nil
}

func cutspace(s string) string {
	for len(s) > 0 && s[0] == ' ' {
		s = s[1:]
	}
	return s
}

// 跳过从值中删除给定前缀，
// 将空格字符的运行视为等效。
func skip(value, prefix string) (string, error) {
	for len(prefix) > 0 {
		if prefix[0] == ' ' {
			if len(value) > 0 && value[0] != ' ' {
				return value, errBad
			}
			prefix = cutspace(prefix)
			value = cutspace(value)
			continue
		}
		if len(value) == 0 || value[0] != prefix[0] {
			return value, errBad
		}
		prefix = prefix[1:]
		value = value[1:]
	}
	return value, nil
}

// Parse解析格式化字符串并返回其表示的时间值。
// 请参阅名为Layout的常量的文档，了解如何表示格式。第二个参数必须可以使用
// 作为第一个参数提供的格式字符串（布局）进行分析。
// 
// 时间的例子。Format详细演示了布局字符串
// 的工作原理，是一个很好的参考。
// 
// 解析（仅限）时，输入可能会在秒字段之后立即包含一个小数秒的
// 字段，即使布局不表示其存在。在这种情况下，逗号或小数点
// 后跟一个最大数字系列被解析为小数秒。
// 分数秒被截断为纳秒精度。
// 
// 假设布局中省略的元素为零，或者
// 零是不可能的，1，因此解析“3:04pm”返回对应于0年1月1日15:04:00 UTC的时间
// 请注意，因为该年是
// 0，所以该时间在零时间之前。
// 年数必须在0000范围内。。9999.检查一周中的某一天的语法
// 但在其他情况下忽略它。
// 
// 对于指定两位数年份06的布局，值NN>=69将被视为19NN，值NN<69将被视为20NN。
// 
// 此评论的其余部分描述了时区的处理。
// 
// 如果没有时区指示器，Parse将返回UTC时间。
// 
// 当解析具有区域偏移量（如-0700）的时间时，如果偏移量对应于当前位置（本地）使用的时区，则Parse在返回的时间中使用该
// 位置和区域。否则，它会将时间记录为
// 处于装配位置，时间固定在给定的区域偏移处。
// 
// 当使用区域缩写（如MST）解析时间时，如果区域缩写
// 在当前位置有一个定义的偏移量，则使用该偏移量。
// 区域缩写“UTC”被认为是UTC，无论其位置如何。
// 如果区域缩写未知，Parse会将时间记录为
// 在具有给定区域缩写和零偏移的装配位置。
// 此选项意味着可以使用
// 相同的布局无损地解析和重新格式化这样的时间，但表示中使用的确切瞬间将与实际区域偏移不同。为了避免此类问题，请选择使用数字区域偏移的时间布局
// 或使用ParseInLocation。
func Parse(layout, value string) (Time, error) {
	return parse(layout, value, UTC, Local)
}

// ParseInLocation与Parse类似，但在两个重要方面有所不同。
// 首先，在没有时区信息的情况下，Parse将时间解释为UTC；
// ParseInLocation将时间解释为给定位置的时间。
// 第二，当给定区域偏移量或缩写时，Parse会尝试将其与本地位置匹配；ParseInLocation使用给定的位置。
func ParseInLocation(layout, value string, loc *Location) (Time, error) {
	return parse(layout, value, loc, loc)
}

func parse(layout, value string, defaultLocation, local *Location) (Time, error) {
	alayout, avalue := layout, value
	rangeErrString := "" // 如果某个值超出范围，则设置
	amSet := false       // 我们需要从午夜的小时数中减去12吗？
	pmSet := false       // 我们需要增加12小时吗？

	// 正在施工。
	var (
		year       int
		month      int = -1
		day        int = -1
		yday       int = -1
		hour       int
		min        int
		sec        int
		nsec       int
		z          *Location
		zoneOffset int = -1
		zoneName   string
	)

	// 每次迭代处理一个std值。
	for {
		var err error
		prefix, std, suffix := nextStdChunk(layout)
		stdstr := layout[len(prefix) : len(layout)-len(suffix)]
		value, err = skip(value, prefix)
		if err != nil {
			return Time{}, &ParseError{alayout, avalue, prefix, value, ""}
		}
		if std == 0 {
			if len(value) != 0 {
				return Time{}, &ParseError{alayout, avalue, "", value, ": extra text: " + quote(value)}
			}
			break
		}
		layout = suffix
		var p string
		switch std & stdMask {
		case stdYear:
			if len(value) < 2 {
				err = errBad
				break
			}
			hold := value
			p, value = value[0:2], value[2:]
			year, err = atoi(p)
			if err != nil {
				value = hold
			} else if year >= 69 { // Unix时间从1969年12月31日开始，在某些时区
				year += 1900
			} else {
				year += 2000
			}
		case stdLongYear:
			if len(value) < 4 || !isDigit(value, 0) {
				err = errBad
				break
			}
			p, value = value[0:4], value[4:]
			year, err = atoi(p)
		case stdMonth:
			month, value, err = lookup(shortMonthNames, value)
			month++
		case stdLongMonth:
			month, value, err = lookup(longMonthNames, value)
			month++
		case stdNumMonth, stdZeroMonth:
			month, value, err = getnum(value, std == stdZeroMonth)
			if err == nil && (month <= 0 || 12 < month) {
				rangeErrString = "month"
			}
		case stdWeekDay:
			// 忽略工作日，错误检查除外。
			_, value, err = lookup(shortDayNames, value)
		case stdLongWeekDay:
			_, value, err = lookup(longDayNames, value)
		case stdDay, stdUnderDay, stdZeroDay:
			if std == stdUnderDay && len(value) > 0 && value[0] == ' ' {
				value = value[1:]
			}
			day, value, err = getnum(value, std == stdZeroDay)
			// 请注意，我们允许任何一位或两位数字的天数。
			// 月、日、年组合在我们完成解析后进行验证。
		case stdUnderYearDay, stdZeroYearDay:
			for i := 0; i < 2; i++ {
				if std == stdUnderYearDay && len(value) > 0 && value[0] == ' ' {
					value = value[1:]
				}
			}
			yday, value, err = getnum3(value, std == stdZeroYearDay)
			// 请注意，我们允许任何一位、两位或三位数字的年日。
			// 在我们完成解析后，将验证年-日-年组合。
		case stdHour:
			hour, value, err = getnum(value, false)
			if hour < 0 || 24 <= hour {
				rangeErrString = "hour"
			}
		case stdHour12, stdZeroHour12:
			hour, value, err = getnum(value, std == stdZeroHour12)
			if hour < 0 || 12 < hour {
				rangeErrString = "hour"
			}
		case stdMinute, stdZeroMinute:
			min, value, err = getnum(value, std == stdZeroMinute)
			if min < 0 || 60 <= min {
				rangeErrString = "minute"
			}
		case stdSecond, stdZeroSecond:
			sec, value, err = getnum(value, std == stdZeroSecond)
			if sec < 0 || 60 <= sec {
				rangeErrString = "second"
				break
			}
			// 特殊情况：我们有小数秒，但没有
			// 格式中的小数秒吗？
			if len(value) >= 2 && commaOrPeriod(value[0]) && isDigit(value, 1) {
				_, std, _ = nextStdChunk(layout)
				std &= stdMask
				if std == stdFracSecond0 || std == stdFracSecond9 {
					// 布局中的小数秒；正常进行
					break
				}
				// 布局中没有小数秒，但输入中有一秒。
				n := 2
				for ; n < len(value) && isDigit(value, n); n++ {
				}
				nsec, rangeErrString, err = parseNanoseconds(value, n)
				value = value[n:]
			}
		case stdPM:
			if len(value) < 2 {
				err = errBad
				break
			}
			p, value = value[0:2], value[2:]
			switch p {
			case "PM":
				pmSet = true
			case "AM":
				amSet = true
			default:
				err = errBad
			}
		case stdpm:
			if len(value) < 2 {
				err = errBad
				break
			}
			p, value = value[0:2], value[2:]
			switch p {
			case "pm":
				pmSet = true
			case "am":
				amSet = true
			default:
				err = errBad
			}
		case stdISO8601TZ, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ShortTZ, stdISO8601ColonSecondsTZ, stdNumTZ, stdNumShortTZ, stdNumColonTZ, stdNumSecondsTz, stdNumColonSecondsTZ:
			if (std == stdISO8601TZ || std == stdISO8601ShortTZ || std == stdISO8601ColonTZ) && len(value) >= 1 && value[0] == 'Z' {
				value = value[1:]
				z = UTC
				break
			}
			var sign, hour, min, seconds string
			if std == stdISO8601ColonTZ || std == stdNumColonTZ {
				if len(value) < 6 {
					err = errBad
					break
				}
				if value[3] != ':' {
					err = errBad
					break
				}
				sign, hour, min, seconds, value = value[0:1], value[1:3], value[4:6], "00", value[6:]
			} else if std == stdNumShortTZ || std == stdISO8601ShortTZ {
				if len(value) < 3 {
					err = errBad
					break
				}
				sign, hour, min, seconds, value = value[0:1], value[1:3], "00", "00", value[3:]
			} else if std == stdISO8601ColonSecondsTZ || std == stdNumColonSecondsTZ {
				if len(value) < 9 {
					err = errBad
					break
				}
				if value[3] != ':' || value[6] != ':' {
					err = errBad
					break
				}
				sign, hour, min, seconds, value = value[0:1], value[1:3], value[4:6], value[7:9], value[9:]
			} else if std == stdISO8601SecondsTZ || std == stdNumSecondsTz {
				if len(value) < 7 {
					err = errBad
					break
				}
				sign, hour, min, seconds, value = value[0:1], value[1:3], value[3:5], value[5:7], value[7:]
			} else {
				if len(value) < 5 {
					err = errBad
					break
				}
				sign, hour, min, seconds, value = value[0:1], value[1:3], value[3:5], "00", value[5:]
			}
			var hr, mm, ss int
			hr, err = atoi(hour)
			if err == nil {
				mm, err = atoi(min)
			}
			if err == nil {
				ss, err = atoi(seconds)
			}
			zoneOffset = (hr*60+mm)*60 + ss // 偏移量以秒为单位
			switch sign[0] {
			case '+':
			case '-':
				zoneOffset = -zoneOffset
			default:
				err = errBad
			}
		case stdTZ:
			// 它看起来像时区吗？
			if len(value) >= 3 && value[0:3] == "UTC" {
				z = UTC
				value = value[3:]
				break
			}
			n, ok := parseTimeZone(value)
			if !ok {
				err = errBad
				break
			}
			zoneName, value = value[:n], value[n:]

		case stdFracSecond0:
			// stdFracSecond0需要布局中指定的确切位数。
			ndigit := 1 + digitsLen(std)
			if len(value) < ndigit {
				err = errBad
				break
			}
			nsec, rangeErrString, err = parseNanoseconds(value, ndigit)
			value = value[ndigit:]

		case stdFracSecond9:
			if len(value) < 2 || !commaOrPeriod(value[0]) || value[1] < '0' || '9' < value[1] {
				// 省略小数秒。
				break
			}
			// 取任意数字，甚至比要求的数字还要多，因为这是stdSecond案例所做的。
			i := 0
			for i < 9 && i+1 < len(value) && '0' <= value[i+1] && value[i+1] <= '9' {
				i++
			}
			nsec, rangeErrString, err = parseNanoseconds(value, 1+i)
			value = value[1+i:]
		}
		if rangeErrString != "" {
			return Time{}, &ParseError{alayout, avalue, stdstr, value, ": " + rangeErrString + " out of range"}
		}
		if err != nil {
			return Time{}, &ParseError{alayout, avalue, stdstr, value, ""}
		}
	}
	if pmSet && hour < 12 {
		hour += 12
	} else if amSet && hour == 12 {
		hour = 0
	}

	// 将yday转换为day，month。
	if yday >= 0 {
		var d int
		var m int
		if isLeap(year) {
			if yday == 31+29 {
				m = int(February)
				d = 29
			} else if yday > 31+29 {
				yday--
			}
		}
		if yday < 1 || yday > 365 {
			return Time{}, &ParseError{alayout, avalue, "", value, ": day-of-year out of range"}
		}
		if m == 0 {
			m = (yday-1)/31 + 1
			if int(daysBefore[m]) < yday {
				m++
			}
			d = yday - int(daysBefore[m-1])
		}
		// 如果已经看到月、日，则yday的m、d必须匹配。
		// 否则，从m，d设置它们。
		if month >= 0 && month != m {
			return Time{}, &ParseError{alayout, avalue, "", value, ": day-of-year does not match month"}
		}
		month = m
		if day >= 0 && day != d {
			return Time{}, &ParseError{alayout, avalue, "", value, ": day-of-year does not match day"}
		}
		day = d
	} else {
		if month < 0 {
			month = int(January)
		}
		if day < 0 {
			day = 1
		}
	}

	// 验证当月的日期。
	if day < 1 || day > daysIn(Month(month), year) {
		return Time{}, &ParseError{alayout, avalue, "", value, ": day out of range"}
	}

	if z != nil {
		return Date(year, Month(month), day, hour, min, sec, nsec, z), nil
	}

	if zoneOffset != -1 {
		t := Date(year, Month(month), day, hour, min, sec, nsec, UTC)
		t.addSec(-int64(zoneOffset))

		// 寻找具有给定偏移量的局部区域。
		// 如果该区域在给定时间有效，请使用它。
		name, offset, _, _, _ := local.lookup(t.unixSec())
		if offset == zoneOffset && (zoneName == "" || name == zoneName) {
			t.setLoc(local)
			return t, nil
		}

		// 否则创建假区域以记录偏移量。
		t.setLoc(FixedZone(zoneName, zoneOffset))
		return t, nil
	}

	if zoneName != "" {
		t := Date(year, Month(month), day, hour, min, sec, nsec, UTC)
		// 寻找具有给定偏移量的局部区域。
		// 如果该区域在给定时间有效，请使用它。
		offset, ok := local.lookupName(zoneName, t.unixSec())
		if ok {
			t.addSec(-int64(offset))
			t.setLoc(local)
			return t, nil
		}

		// 否则，创建偏移量未知的假区域。
		if len(zoneName) > 3 && zoneName[:3] == "GMT" {
			offset, _ = atoi(zoneName[3:]) // 由parseGMT保证正常。
			offset *= 3600
		}
		t.setLoc(FixedZone(zoneName, offset))
		return t, nil
	}

	// 否则，退回默认状态。
	return Date(year, Month(month), day, hour, min, sec, nsec, defaultLocation), nil
}

// parseTimeZone解析时区字符串并返回其长度。时区
// 是人为产生的，不可预测。我们无法进行精确的错误检查。
// 另一方面，为了正确解析，
// 字符串的开头，所以几乎总是有一个
// 在那里。我们查看字符串开头的一系列大写字母。
// 如果超过5，则为错误。
// 如果有4个或5个，最后一个是T，那就是时区。
// 如果有3个，那就是一个时区。
// 除特殊情况外，这里不是时区。
// GMT是特殊的，因为它可以有一个小时的偏移量。
func parseTimeZone(value string) (length int, ok bool) {
	if len(value) < 3 {
		return 0, false
	}
	// 特例1：ChST和MeST是唯一具有小写字母的区域。
	if len(value) >= 4 && (value[:4] == "ChST" || value[:4] == "MeST") {
		return 4, true
	}
	// 特殊情况2:GMT可能有一个小时的偏移量；特别对待它。
	if value[:3] == "GMT" {
		length = parseGMT(value)
		return length, true
	}
	// 特例3：一些时区没有命名，但有+/-00格式
	if value[0] == '+' || value[0] == '-' {
		length = parseSignedOffset(value)
		ok := length > 0 // 如果输入错误，parseSignedOffset返回0 
		return length, ok
	}
	// 有多少个大写字母？至少需要三个，最多五个。
	var nUpper int
	for nUpper = 0; nUpper < 6; nUpper++ {
		if nUpper >= len(value) {
			break
		}
		if c := value[nUpper]; c < 'A' || 'Z' < c {
			break
		}
	}
	switch nUpper {
	case 0, 1, 2, 6:
		return 0, false
	case 5: // 必须以T结尾才能匹配。
		if value[4] == 'T' {
			return 5, true
		}
	case 4:
		// 必须以T结尾，除了一个特殊情况。
		if value[3] == 'T' || value[:4] == "WITA" {
			return 4, true
		}
	case 3:
		return 3, true
	}
	return 0, false
}

// parseGMT解析GMT时区。已知输入字符串以“GMT”开头。
// 该函数检查在
// 范围-23到+23（不包括零）中，后面是否有符号和数字。
func parseGMT(value string) int {
	value = value[3:]
	if len(value) == 0 {
		return 3
	}

	return 3 + parseSignedOffset(value)
}

// parseSignedOffset解析带符号的时区偏移量（例如“+03”或“-04”）。
// 该函数检查-23到+23范围内的有符号数，不包括零。
// 返回找到的偏移字符串的长度，否则返回0 
func parseSignedOffset(value string) int {
	sign := value[0]
	if sign != '-' && sign != '+' {
		return 0
	}
	x, rem, err := leadingInt(value[1:])

	// 如果引线没有消耗任何内容，则返回失败
	if err != nil || value[1:] == rem {
		return 0
	}
	if x > 23 {
		return 0
	}
	return len(value) - len(rem)
}

func commaOrPeriod(b byte) bool {
	return b == '.' || b == ','
}

func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, err error) {
	if !commaOrPeriod(value[0]) {
		err = errBad
		return
	}
	if nbytes > 10 {
		value = value[:10]
		nbytes = 10
	}
	if ns, err = atoi(value[1:nbytes]); err != nil {
		return
	}
	if ns < 0 {
		rangeErrString = "fractional second"
		return
	}
	// 我们需要纳秒，这意味着按格式中缺失数字的数量
	// 缩放，最大长度为10。
	scaleDigits := 10 - nbytes
	for i := 0; i < scaleDigits; i++ {
		ns *= 10
	}
	return
}

var errLeadingInt = errors.New("time: bad [0-9]*") // 从未打印过

// leadingInt使用s.
func leadingInt(s string) (x uint64, rem string, err error) {
	i := 0
	for ; i < len(s); i++ {
		c := s[i]
		if c < '0' || c > '9' {
			break
		}
		if x > 1<<63/10 {
			// overflow 
			return 0, "", errLeadingInt
		}
		x = x*10 + uint64(c) - '0'
		if x > 1<<63 {
			// overflow 
			return 0, "", errLeadingInt
		}
	}
	return x, s[i:], nil
}

// leadingFraction使用s.
// 它仅用于分数，因此不会返回溢出错误，
// 它只是停止累积精度。
func leadingFraction(s string) (x uint64, scale float64, rem string) {
	i := 0
	scale = 1
	overflow := false
	for ; i < len(s); i++ {
		c := s[i]
		if c < '0' || c > '9' {
			break
		}
		if overflow {
			continue
		}
		if x > (1<<63-1)/10 {
			// 溢出可能会给出一个正数，所以要小心。
			overflow = true
			continue
		}
		y := x*10 + uint64(c) - '0'
		if y > 1<<63 {
			overflow = true
			continue
		}
		x = y
		scale *= 10
	}
	return x, scale, s[i:]
}

var unitMap = map[string]uint64{
	"ns": uint64(Nanosecond),
	"us": uint64(Microsecond),
	"µs": uint64(Microsecond), // U+00B5=微符号
	"μs": uint64(Microsecond), // U+03BC=希腊字母mu 
	"ms": uint64(Millisecond),
	"s":  uint64(Second),
	"m":  uint64(Minute),
	"h":  uint64(Hour),
}

// ParseDuration解析持续时间字符串。
// 持续时间字符串可能是一个带符号的序列，由
// 十进制数字组成，每个数字都有可选的分数和单位后缀，例如“300ms”、“1.5h”或“2h45m”。
// 有效时间单位为“ns”、“us”（或“µs”）、“ms”、“s”、“m”、“h”。
func ParseDuration(s string) (Duration, error) {
	// /[-+]？([0-9]*(\.[0-9]*)?[a-z]+）+
	orig := s
	var d uint64
	neg := false

	// 消费[-+]？
	if s != "" {
		c := s[0]
		if c == '-' || c == '+' {
			neg = c == '-'
			s = s[1:]
		}
	}
	// 特殊情况：如果剩下的都是“0”，这就是零。
	if s == "0" {
		return 0, nil
	}
	if s == "" {
		return 0, errors.New("time: invalid duration " + quote(orig))
	}
	for s != "" {
		var (
			v, f  uint64      // 小数点前后的整数
			scale float64 = 1 // value=v+f/scale 
		)

		var err error

		// 下一个字符必须是[0-9]
		if !(s[0] == '.' || '0' <= s[0] && s[0] <= '9') {
			return 0, errors.New("time: invalid duration " + quote(orig))
		}
		// 消费[0-9]*
		pl := len(s)
		v, s, err = leadingInt(s)
		if err != nil {
			return 0, errors.New("time: invalid duration " + quote(orig))
		}
		pre := pl != len(s) // 我们在一段时间之前是否消费过任何东西

		// 消费（\[0-9]*）？
		post := false
		if s != "" && s[0] == '.' {
			s = s[1:]
			pl := len(s)
			f, scale, s = leadingFraction(s)
			post = pl != len(s)
		}
		if !pre && !post {
			// 无数字（例如“.s”或“-.s”）
			return 0, errors.New("time: invalid duration " + quote(orig))
		}

		// 消耗单位。
		i := 0
		for ; i < len(s); i++ {
			c := s[i]
			if c == '.' || '0' <= c && c <= '9' {
				break
			}
		}
		if i == 0 {
			return 0, errors.New("time: missing unit in duration " + quote(orig))
		}
		u := s[:i]
		s = s[i:]
		unit, ok := unitMap[u]
		if !ok {
			return 0, errors.New("time: unknown unit " + quote(u) + " in duration " + quote(orig))
		}
		if v > 1<<63/unit {
			// 溢出
			return 0, errors.New("time: invalid duration " + quote(orig))
		}
		v *= unit
		if f > 0 {
			// 浮点64需要在几小时内达到纳秒级的精度。
			// v>=0&&（f*单位/刻度）<=3.6e+12（ns/h，h为最大单位）
			v += uint64(float64(f) * (float64(unit) / scale))
			if v > 1<<63 {
				// 溢出
				return 0, errors.New("time: invalid duration " + quote(orig))
			}
		}
		d += v
		if d > 1<<63 {
			return 0, errors.New("time: invalid duration " + quote(orig))
		}
	}
	if neg {
		return -Duration(d), nil
	}
	if d > 1<<63-1 {
		return 0, errors.New("time: invalid duration " + quote(orig))
	}
	return Duration(d), nil
}
